// Wikipedia: https://en.wikipedia.org/wiki/Knight%27s_tour

class OpenKnightTour {
  constructor(size) {
    // Constructor to initialize the chessboard and size
    this.board = new Array(size).fill(0).map(() => new Array(size).fill(0))
    this.size = size
  }

  getMoves([i, j]) {
    // Helper function to get the valid moves of the knight from the current position
    const moves = [
      [i + 2, j - 1],
      [i + 2, j + 1],
      [i - 2, j - 1],
      [i - 2, j + 1],
      [i + 1, j - 2],
      [i + 1, j + 2],
      [i - 1, j - 2],
      [i - 1, j + 2]
    ]

    // Filter out moves that are within the board boundaries
    return moves.filter(
      ([y, x]) => y >= 0 && y < this.size && x >= 0 && x < this.size
    )
  }

  isComplete() {
    // Helper function to check if the board is complete
    return !this.board.map((row) => row.includes(0)).includes(true)
  }

  solve() {
    // Function to find the solution for the given board
    for (let i = 0; i < this.size; i++) {
      for (let j = 0; j < this.size; j++) {
        if (this.solveHelper([i, j], 0)) return true
      }
    }
    return false
  }

  solveHelper([i, j], curr) {
    // Helper function for the main computation
    if (this.isComplete()) return true

    // Iterate through possible moves and attempt to fill the board
    for (const [y, x] of this.getMoves([i, j])) {
      if (this.board[y][x] === 0) {
        this.board[y][x] = curr + 1
        if (this.solveHelper([y, x], curr + 1)) return true
        // Backtracking: If the solution is not found, reset the cell to 0
        this.board[y][x] = 0
      }
    }
    return false
  }

  printBoard(output = (value) => console.log(value)) {
    // Utility function to display the board
    for (const row of this.board) {
      let string = ''
      for (const elem of row) {
        string += elem + '\t'
      }
      output(string)
    }
  }
}

export { OpenKnightTour }
